// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package par

import "fmt"

// 队列管理一组并行执行的工作项。
// 活动工作项的数量有限，多余的工作项按顺序排队。
type Queue struct {
	maxActive int
	st        chan queueState
}

type queueState struct {
	active  int // goroutine处理工作的数量；当len（backlog）>0 
	backlog []func()
	idle    chan struct{} // 如果非nil，当active变为0 
}

// NewQueue返回一个并行执行最多maxActive项的队列。
// 
// maxActive必须为正。
func NewQueue(maxActive int) *Queue {
	if maxActive < 1 {
		panic(fmt.Sprintf("par.NewQueue called with nonpositive limit (%d)", maxActive))
	}

	q := &Queue{
		maxActive: maxActive,
		st:        make(chan queueState, 1),
	}
	q.st <- queueState{}
	return q
}

// 将f添加为队列中的工作项。
// 
// Add立即返回，但队列将被标记为非空闲，直到
// f（以及任何后续添加的工作）完成。
func (q *Queue) Add(f func()) {
	st := <-q.st
	if st.active == q.maxActive {
		st.backlog = append(st.backlog, f)
		q.st <- st
		return
	}
	if st.active == 0 {
		// 将q标记为非空闲。
		st.idle = nil
	}
	st.active++
	q.st <- st

	go func() {
		for {
			f()

			st := <-q.st
			if len(st.backlog) == 0 {
				if st.active--; st.active == 0 && st.idle != nil {
					close(st.idle)
				}
				q.st <- st
				return
			}
			f, st.backlog = st.backlog[0], st.backlog[1:]
			q.st <- st
		}
	}()
}

// Idle返回一个通道，当q没有（活动或排队）
// 未完成的工作时，该通道将关闭。
func (q *Queue) Idle() <-chan struct{} {
	st := <-q.st
	defer func() { q.st <- st }()

	if st.idle == nil {
		st.idle = make(chan struct{})
		if st.active == 0 {
			close(st.idle)
		}
	}

	return st.idle
}
